﻿using DimensionsHelper.Common.Contracts.Metadata;

namespace DimensionsHelper.Client.WinUi.ViewModels;

public partial class QueryViewModel : ObservableObject
{
    private const string SettingsKey = "Query";

    private static WeakReference<IProjectTask>? ProjectTask;
    private readonly IOptionalFileService _optionalFileService;

    private readonly ConditionalWeakTable<DataTable, string> _queries;


    public QueryViewModel()
    {
        _optionalFileService = App.GetService<IOptionalFileService>();
        _optionalFileService.Key = SettingsKey;
        _queries = [];
    }

    public bool IsQueryFinishedHandled
    {
        get;
        set;
    }


    public ObservableCollection<string> FieldNames { get; } = [];

    public ObservableCollection<string> SelectedFieldNames { get; } = [];


    [ObservableProperty]
    public partial DataSet QueryResult { get; set; } = new();


    public ObservableCollection<DataTable> QueryTables { get; } = [];


    [ObservableProperty]
    public partial DataTable? CurrentTable { get; set; }


    [ObservableProperty]
    public partial string MetadataFile { get; set; } = string.Empty;


    [ObservableProperty]
    public partial string MetadataFileDisplayText { get; set; } = "未选择元数据(.mdd)文件";


    [ObservableProperty]
    public partial DataCollectionQueryCategoryDataType ValueType { get; set; } =
        DataCollectionQueryCategoryDataType.Name;


    [ObservableProperty]
    public partial string CasedataFile { get; set; } = string.Empty;


    [ObservableProperty]
    public partial string CasedataFileDisplayText { get; set; } = "未选择数据库(.ddf)文件";


    [ObservableProperty]
    public partial string QueryText { get; set; } = string.Empty;


    [ObservableProperty]
    public partial bool IsQuerying
    {
        get;
        set;
    }

    public static IProjectTask GetProjectTask()
    {
        if (ProjectTask == null || !ProjectTask.TryGetTarget(out IProjectTask? projectTask))
        {
            projectTask = App.GetService<IProjectTaskService>().Allocate(SettingsKey, true);
            ProjectTask = new WeakReference<IProjectTask>(projectTask);
        }

        return projectTask;
    }

    private static void FillFieldCollection(ObservableCollection<string> collection, IField field, string? prefix)
    {
        switch (field)
        {
            case IArray { IteratorType: IteratorTypes.Categorical } categoricalLoop:
            {
                foreach (IField subField in categoricalLoop.Class.Fields)
                {
                    foreach (IElement element in categoricalLoop.Categories)
                    {
                        FillFieldCollection(collection, subField, $"{prefix}{field.Name}[{{{element.Name}}}].");
                    }
                }

                break;
            }

            case IArray
            {
                IteratorType: IteratorTypes.NumericRanges, Ranges: { LowerBound: int l, UpperBound: int u }
            } numericLoop:
            {
                foreach (IField subField in numericLoop.Class.Fields)
                {
                    for (int i = l; i <= u; i++)
                    {
                        FillFieldCollection(collection, subField, $"{prefix}{field.Name}[{i}].");
                    }
                }

                break;
            }

            case IBlock block:
            {
                foreach (IField subField in block.Fields)
                {
                    FillFieldCollection(collection, subField, $"{prefix}{field.Name}.");
                }

                break;
            }

            case IField:
            {
                collection.Add($"{prefix}{field.Name}");
                break;
            }
        }
    }


    private async Task BuildFieldListFromMetadata()
    {
        if (string.IsNullOrEmpty(MetadataFile))
        {
            return;
        }

        using CancellationTokenSource tokenSource = new();
        tokenSource.CancelAfter(1000);

        IMetadataSerializer serializer = App.GetService<IMetadataSerializer>();
        IMetadataDocument? document = await Task.Run(() => serializer.Deserialize(MetadataFile), tokenSource.Token)
            .ContinueWith(task =>
            {
                if (task.IsCanceled)
                {
                    return null;
                }

                return task.Result;
            });

        if (document == null)
        {
            await App.GetService<IMessageService>().ShowAsync(ResourceKeys.AppTaskTimeout.GetLocalized(), null);
            return;
        }

        GetProjectTask().Log($"已读取 {document.Fields.Count} 个字段。");

        FieldNames.Clear();
        SelectedFieldNames.Clear();
        foreach (IField field in document.Fields)
        {
            FillFieldCollection(FieldNames, field, null);
        }
    }


    async partial void OnMetadataFileChanged(string value)
    {
        try
        {
            if (!string.IsNullOrEmpty(value))
            {
                await BuildFieldListFromMetadata();
                MetadataFileDisplayText = $"当前元数据（.mdd）文件：{value}";
            }
        }
        catch (Exception e)
        {
            GetProjectTask().Error(e.Message);
            App.GetService<INavigationService>()
                .NavigateTo(typeof(TaskInformationViewModel).FullName!, SettingsKey);
        }
    }


    [RelayCommand]
    private async Task SelectMetadataFile()
    {
        string? fileName = await App.GetService<IFilePikerService>()
            .PickSingleFileAsync(FileFilters.Mdd);

        if (!string.IsNullOrEmpty(fileName))
        {
            MetadataFile = fileName;
        }
    }


    partial void OnCasedataFileChanged(string value)
    {
        if (!string.IsNullOrEmpty(value))
        {
            CasedataFileDisplayText = $"当前数据库（.ddf）文件：{value}";
            ;
        }
    }


    [RelayCommand]
    private async Task SelectCasedataFile()
    {
        string? fileName = await App.GetService<IFilePikerService>().PickSingleFileAsync(FileFilters.Ddf);
        if (!string.IsNullOrEmpty(fileName))
        {
            CasedataFile = fileName;
        }
    }


    public void RemoveDataTable(string? tableName)
    {
        if (string.IsNullOrEmpty(tableName))
        {
            return;
        }

        DataTable? dataTable = QueryResult.Tables[tableName];

        if (dataTable == null || dataTable.DataSet != QueryResult)
        {
            return;
        }

        QueryTables.Remove(dataTable);
        QueryResult.Tables.Remove(dataTable);

        if (CurrentTable == dataTable)
        {
            CurrentTable = QueryTables.FirstOrDefault();
        }

        _queries.Remove(dataTable);
    }


    public string GetDataTableQuery(DataTable table) => _queries.TryGetValue(table, out string? text) ? text : "无";


    private string GetQueryTableName()
    {
        string metadataName = Path.GetFileNameWithoutExtension(MetadataFile);
        string casedataName = Path.GetFileNameWithoutExtension(CasedataFile);
        string fullName = $"{metadataName};{casedataName}";

        string finalName = fullName;
        int count = 1;

        while (QueryResult.Tables.Contains(finalName))
        {
            finalName = $"{fullName}`{count}";
            count++;
        }

        return finalName;
    }


    [RelayCommand]
    private void StartQuerying()
    {
        if (string.IsNullOrEmpty(MetadataFile) || string.IsNullOrEmpty(CasedataFile))
        {
            return;
        }

        IsQuerying = true;

        string query = QueryText.Trim();
        string connectString = DataCollectionHelper.BuildConnectionString(MetadataFile, CasedataFile, ValueType);
        string tableName = GetQueryTableName();

        Task.Run(async () =>
        {
            string metadata = MetadataFile;
            string casedata = CasedataFile;

            await DataCollectionHelper.LoadDataAsync(connectString,
                string.Join(",", SelectedFieldNames),
                query,
                QueryResult,
                tableName);

            if (QueryResult.Tables[tableName] is { } dataTable)
            {
                App.DispatcherQueue.TryEnqueue(() => QueryTables.Add(dataTable));

                if (!string.IsNullOrEmpty(query))
                {
                    _queries.AddOrUpdate(dataTable, query);
                }

                return new QueryInfo
                {
                    Metadata = metadata,
                    Casedata = casedata,
                    Query = query,
                    Data = dataTable
                };
            }

            return null;
        }).ContinueWith(t =>
        {
            if (t.Result is { Data: not null } info)
            {
                App.GetService<QueryDataViewModel>()
                    .AddDataTable(info.Data, info.Metadata, info.Casedata, info.Query);

                App.DispatcherQueue.TryEnqueue(() =>
                {
                    App.GetService<INavigationService>()
                        .NavigateTo(typeof(QueryDataViewModel).FullName!, info.Data.TableName);
                });
            }

            if (!IsQueryFinishedHandled)
            {
                App.DispatcherQueue.TryEnqueue(() => IsQuerying = false);
            }
        });
    }


    private class QueryInfo
    {
        public string Metadata { get; set; } = string.Empty;

        public string Casedata { get; set; } = string.Empty;

        public string Query { get; set; } = string.Empty;

        public DataTable? Data { get; set; }
    }
}